/*
 * AbstractPlot.cpp
 *
 * Copyright 2012 Pieter Agten
 *
 * This file is part of tltgen.
 *
 * tltgen is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * tltgen is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with tltgen.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * @file   AbstractPlot.cpp
 * @author Pieter Agten (pieter.agten@gmail.com)
 * @date   7 jan 2013
 */


#include "wxPLplotwindow.h"

#include <boost/lexical_cast.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/signals2.hpp>
#include <cmath>

#include "AbstractPlot.hpp"

using boost::tuple;
using boost::make_tuple;

namespace tltgen {

  AbstractPlot::~AbstractPlot() { }

  bool AbstractPlot::isValidDatasetIndex(unsigned int i) const
  {
    return 0 <= i && i < getNbDatasets();
  }
  
  tuple<PLFLT, PLFLT> AbstractPlot::getXBounds() const
  {
    return tuple<PLFLT, PLFLT>(this->xmin, this->xmax);
  }

  bool AbstractPlot::isValidXBounds(PLFLT min, PLFLT max) const
  {
    return std::isfinite(min) && std::isfinite(max) && min < max;
  }

  void AbstractPlot::setXBounds(PLFLT min, PLFLT max, bool signal)
  {
    if (! isValidXBounds(min, max)) {
      throw std::invalid_argument("Values (" + boost::lexical_cast<std::string>(min) + "," + boost::lexical_cast<std::string>(max) +
				  ") are invalid bounds for this plot's X axis.");
    }

    PLFLT prevMin = this->xmin;
    PLFLT prevMax = this->xmax;
    this->xmin = min;
    this->xmax = max;
    this->xBoundsAuto = false;
    if (signal && (this->xmin != prevMin || this->xmax != prevMax)) {
      this->signalDataChanged();
    }
  }

  void AbstractPlot::setXBoundsAuto(bool signal)
  {
    PLFLT prevMin = this->xmin;
    PLFLT prevMax = this->xmax;
    tuple<PLFLT, PLFLT> xBounds = getXBoundsAuto();
    if (isValidXBounds(xBounds.get<0>(), xBounds.get<1>())) {
      PLFLT length = (xBounds.get<1>() - xBounds.get<0>());
      PLFLT margin = length/20;
      tuple<PLFLT, PLFLT> xBoundsWithMargin = make_tuple(xBounds.get<0>() - margin, xBounds.get<1>() + margin);
      this->xmin = xBoundsWithMargin.get<0>();
      this->xmax = xBoundsWithMargin.get<1>();
    } else {
      this->xmin = 0;
      this->xmax = 0;
    }
    this->xBoundsAuto = true;
    if (signal && (this->xmin != prevMin || this->xmax != prevMax)) {
      this->signalDataChanged();
    }
  }

  bool AbstractPlot::isXBoundsAuto() const
  {
    return this->xBoundsAuto;
  }

  tuple<PLFLT, PLFLT> AbstractPlot::getYBounds() const
  {
    return tuple<PLFLT, PLFLT>(this->ymin, this->ymax);
  }

  bool AbstractPlot::isValidYBounds(PLFLT min, PLFLT max) const
  {
    return std::isfinite(min) && std::isfinite(max) && min < max;
  }

  void AbstractPlot::setYBounds(PLFLT min, PLFLT max, bool signal)
  {
    if (! isValidYBounds(min, max)) {
      throw std::invalid_argument("Values (" + boost::lexical_cast<std::string>(min) + "," + boost::lexical_cast<std::string>(max) +
				  ") are invalid bounds for this plot's Y axis.");
    }

    PLFLT prevMin = this->ymin;
    PLFLT prevMax = this->ymax;
    this->ymin = min;
    this->ymax = max;
    this->yBoundsAuto = false;
    if (signal && (this->ymin != prevMin || this->ymax != prevMax)) {
      this->signalDataChanged();
    }
  }

  void AbstractPlot::setYBoundsAuto(bool signal)
  {
    PLFLT prevMin = this->ymin;
    PLFLT prevMax = this->ymax;
    tuple<PLFLT, PLFLT> yBounds = getYBoundsAuto();
    if (isValidYBounds(yBounds.get<0>(), yBounds.get<1>())) {
      PLFLT length = (yBounds.get<1>() - yBounds.get<0>());
      PLFLT margin = length/20;
      tuple<PLFLT, PLFLT> yBoundsWithMargin = make_tuple(yBounds.get<0>() - margin, yBounds.get<1>() + margin);
      this->ymin = yBoundsWithMargin.get<0>();
      this->ymax = yBoundsWithMargin.get<1>();
    } else {
      this->ymin = 0;
      this->ymax = 0;
    }
    this->yBoundsAuto = true;
    if (signal && (this->ymin != prevMin || this->ymax != prevMax)) {
      this->signalDataChanged();
    }
  }
  bool AbstractPlot::isYBoundsAuto() const
  {
    return this->yBoundsAuto;
  }


  boost::signals2::connection AbstractPlot::connectDataChangedObserver(const DataChangedSlot &slot)
  {
    return this->signalDataChanged.connect(slot);
  }

  boost::signals2::connection AbstractPlot::connectNbDatasetsChangedObserver(const NbDatasetsChangedSlot &slot)
  {
    return this->signalNbDatasetsChanged.connect(slot);
  }

}

